home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / hplip / ui / scrollfax.py < prev    next >
Text File  |  2008-10-13  |  50KB  |  1,287 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2001-2008 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # Local
  23. from base.g import *
  24. from base import utils, magic
  25. from prnt import cups
  26. from ui_utils import load_pixmap
  27.  
  28. # Qt
  29. from qt import *
  30. from scrollview import ScrollView, PixmapLabelButton
  31. from allowabletypesdlg import AllowableTypesDlg
  32. from waitform import WaitForm
  33.  
  34. # Std Lib
  35. import os.path, os
  36. import struct, Queue, time
  37.  
  38.  
  39. fax_enabled = prop.fax_build
  40.  
  41. if fax_enabled:
  42.     try:
  43.         from fax import fax
  44.     except ImportError:
  45.         # This can fail on Python < 2.3 due to the datetime module
  46.         # or if fax was diabled during the build
  47.         log.warn("Fax send disabled - Python 2.3+ required.")
  48.         fax_enabled = False
  49.     
  50.     
  51. coverpages_enabled = False
  52. if fax_enabled:
  53.     try:
  54.         import reportlab
  55.         ver = reportlab.Version
  56.         try:
  57.             ver_f = float(ver)
  58.         except ValueError:
  59.             ver_f = 0.0
  60.             
  61.         if ver_f >= 2.0:
  62.             coverpages_enabled = True
  63.         else:
  64.             log.warn("Pre-2.0 version of Reportlab installed. Fax coverpages disabled.")
  65.             
  66.     except ImportError:
  67.         log.warn("Reportlab not installed. Fax coverpages disabled.")
  68.         
  69.         
  70. if not coverpages_enabled:
  71.     log.warn("Please install version 2.0+ of Reportlab for coverpage support.")
  72.  
  73. if fax_enabled and coverpages_enabled:
  74.     from fax import coverpages
  75.     from coverpageform import CoverpageForm
  76.     
  77.  
  78. # Used to store MIME types for files
  79. # added directly in interface.
  80. job_types = {} # { job_id : "mime_type", ...}
  81.  
  82. class FileListViewItem(QListViewItem):
  83.     def __init__(self, parent, order, title, mime_type_desc, str_pages, path):
  84.         QListViewItem.__init__(self, parent, order, title, mime_type_desc, str_pages, path)
  85.         self.path = path
  86.  
  87.  
  88. class RecipientListViewItem(QListViewItem):
  89.     def __init__(self, parent, order, name, fax, notes):
  90.         QListViewItem.__init__(self, parent, order, name, fax, notes)
  91.         self.name = name
  92.         #self.entry = entry
  93.  
  94.  
  95. class PhoneNumValidator(QValidator):
  96.     def __init__(self, parent=None, name=None):
  97.         QValidator.__init__(self, parent, name)
  98.  
  99.     def validate(self, input, pos):
  100.         input = unicode(input)
  101.         if not input:
  102.             return QValidator.Acceptable, pos
  103.         elif input[pos-1] not in u'0123456789-(+) *#':
  104.             return QValidator.Invalid, pos
  105.         elif len(input) > 50:
  106.             return QValidator.Invalid, pos
  107.         else:
  108.             return QValidator.Acceptable, pos
  109.  
  110.  
  111.             
  112. class ScrollFaxView(ScrollView):
  113.     def __init__(self, service, parent = None, form=None, name = None,fl = 0):
  114.         ScrollView.__init__(self, service, parent, name, fl)
  115.         
  116.         global fax_enabled
  117.         if service is None:
  118.             fax_enabled = False
  119.             
  120.         self.form = form
  121.         self.file_list = []
  122.         self.pages_button_group = 0
  123.         self.recipient_list = []
  124.         self.username = prop.username
  125.         self.busy = False
  126.         self.allowable_mime_types = cups.getAllowableMIMETypes()
  127.         self.allowable_mime_types.append("application/x-python")
  128.         self.cover_page_func, cover_page_png = None, None
  129.         self.cover_page_message = ''
  130.         self.cover_page_re = ''
  131.         self.cover_page_name = ''
  132.         self.update_queue = Queue.Queue() # UI updates from send thread
  133.         self.event_queue = Queue.Queue() # UI events (cancel) to send thread
  134.         self.prev_selected_file_path = ''
  135.         self.prev_selected_recipient = ''
  136.         self.preserve_formatting = False
  137.         self.waitdlg = None
  138.         log.debug(self.allowable_mime_types)
  139.         self.last_job_id = 0
  140.         self.dev = None
  141.         self.lock_file = None
  142.         
  143.  
  144.         self.db =  fax.FaxAddressBook()
  145.         self.last_db_modification = self.db.last_modification_time()
  146.  
  147.         self.MIME_TYPES_DESC = \
  148.         {
  149.             "application/pdf" : (self.__tr("PDF Document"), '.pdf'),
  150.             "application/postscript" : (self.__tr("Postscript Document"), '.ps'),
  151.             "application/vnd.hp-HPGL" : (self.__tr("HP Graphics Language File"), '.hgl, .hpg, .plt, .prn'),
  152.             "application/x-cshell" : (self.__tr("C Shell Script"), '.csh, .sh'),
  153.             "application/x-csource" : (self.__tr("C Source Code"), '.c'),
  154.             "text/cpp": (self.__tr("C++ Source Code"), '.cpp, .cxx'),
  155.             "application/x-perl" : (self.__tr("Perl Script"), '.pl'),
  156.             "application/x-python" : (self.__tr("Python Program"), '.py'),
  157.             "application/x-shell" : (self.__tr("Shell Script"), '.sh'),
  158.             "text/plain" : (self.__tr("Plain Text"), '.txt, .log, etc'),
  159.             "text/html" : (self.__tr("HTML Dcoument"), '.htm, .html'),
  160.             "image/gif" : (self.__tr("GIF Image"), '.gif'),
  161.             "image/png" : (self.__tr("PNG Image"), '.png'),
  162.             "image/jpeg" : (self.__tr("JPEG Image"), '.jpg, .jpeg'),
  163.             "image/tiff" : (self.__tr("TIFF Image"), '.tif, .tiff'),
  164.             "image/x-bitmap" : (self.__tr("Bitmap (BMP) Image"), '.bmp'),
  165.             "image/x-bmp" : (self.__tr("Bitmap (BMP) Image"), '.bmp'),
  166.             "image/x-photocd" : (self.__tr("Photo CD Image"), '.pcd'),
  167.             "image/x-portable-anymap" : (self.__tr("Portable Image (PNM)"), '.pnm'),
  168.             "image/x-portable-bitmap" : (self.__tr("Portable B&W Image (PBM)"), '.pbm'),
  169.             "image/x-portable-graymap" : (self.__tr("Portable Grayscale Image (PGM)"), '.pgm'),
  170.             "image/x-portable-pixmap" : (self.__tr("Portable Color Image (PPM)"), '.ppm'),
  171.             "image/x-sgi-rgb" : (self.__tr("SGI RGB"), '.rgb'),
  172.             "image/x-xbitmap" : (self.__tr("X11 Bitmap (XBM)"), '.xbm'),
  173.             "image/x-xpixmap" : (self.__tr("X11 Pixmap (XPM)"), '.xpm'),
  174.             "image/x-sun-raster" : (self.__tr("Sun Raster Format"), '.ras'),
  175.         }
  176.         
  177.         
  178.         user_settings = utils.UserSettings()
  179.         self.cmd_fab = user_settings.cmd_fab
  180.         log.debug("FAB command: %s" % self.cmd_fab)
  181.  
  182.         if fax_enabled:
  183.             self.check_timer = QTimer(self, "CheckTimer")
  184.             self.connect(self.check_timer, SIGNAL('timeout()'), self.PeriodicCheck)
  185.             self.check_timer.start(3000)
  186.         
  187.  
  188.     def fillControls(self):
  189.         ScrollView.fillControls(self)
  190.  
  191.         if fax_enabled:
  192.             if self.addPrinterFaxList(): #faxes=True, printers=False):
  193.                 
  194.                 self.addGroupHeading("files_to_fax", self.__tr("File(s) to Fax"))
  195.                 self.addFileList()
  196.     
  197.                 if coverpages_enabled:
  198.                     self.addGroupHeading("coverpage", self.__tr("Add/Edit Fax Coverpage"))
  199.                     self.addCoverpage()
  200.     
  201.                 self.addGroupHeading("recipients", self.__tr("Recipient(s)"))
  202.     
  203.                 self.addRecipientList()
  204.     
  205.                 self.addGroupHeading("recipient_add_from_fab", self.__tr("Add Recipients from the Fax Address Book"))
  206.     
  207.                 self.addRecipientAddFromFAB()
  208.     
  209.                 self.addGroupHeading("recipient_quick_add", self.__tr("<i>Quick Add</i> an Individual Recipient"))
  210.     
  211.                 self.addRecipientQuickAdd()
  212.     
  213.                 self.addGroupHeading("space1", "")
  214.     
  215.                 self.faxButton = self.addActionButton("bottom_nav", self.__tr("Send Fax Now"), 
  216.                                         self.faxButton_clicked, 'fax.png', 'fax-disabled.png', 
  217.                                         self.__tr("Close"), self.funcButton_clicked)
  218.     
  219.                 self.faxButton.setEnabled(False)
  220.     
  221.                 self.updateRecipientCombos()
  222.     
  223.                 self.maximizeControl()
  224.             
  225.             else:
  226.                 QApplication.restoreOverrideCursor()
  227.                 self.form.FailureUI("<b>Fax is disabled.</b><p>No CUPS fax queue found for this device.")
  228.                 self.funcButton_clicked()
  229.  
  230.         else:
  231.             QApplication.restoreOverrideCursor()
  232.             self.form.FailureUI("<b>Fax is disabled.</b><p>Python version 2.3 or greater required or fax was disabled during build.")
  233.             self.funcButton_clicked()
  234.             
  235.             
  236.  
  237.     def onUpdate(self, cur_device=None):
  238.         log.debug("ScrollPrintView.onUpdate()")
  239.         self.updateFileList()
  240.         self.updateRecipientList()
  241.         
  242.  
  243.     def PeriodicCheck(self): # called by check_timer every 3 sec
  244.         #print self
  245.         if not self.busy:
  246.             log.debug("Checking for incoming faxes...")
  247.             
  248.             result = list(self.service.CheckForWaitingFax(self.cur_device.device_uri,
  249.                           prop.username, self.last_job_id))
  250.                 
  251.             fax_file = str(result[7])
  252.             
  253.             if fax_file:
  254.                 self.last_job_id = 0
  255.                 log.debug("A new fax has arrived: %s" % fax_file)
  256.                 job_id = int(result[4])
  257.                 title = str(result[5])
  258.  
  259.                 if self.form.isMinimized():
  260.                     self.form.showNormal()
  261.  
  262.                 self.check_timer.stop()
  263.                 self.addFileFromJob(0, title, prop.username, job_id, fax_file)
  264.                 self.check_timer.start(3000)
  265.                 return
  266.  
  267.             log.debug("Not found.")
  268.             
  269.             # Check for updated FAB
  270.             last_db_modification = self.db.last_modification_time()
  271.  
  272.             if last_db_modification > self.last_db_modification:
  273.                 QApplication.setOverrideCursor(QApplication.waitCursor)
  274.                 log.debug("FAB has been modified. Re-reading...")
  275.                 self.last_db_modification = last_db_modification
  276.                 self.updateRecipientCombos()
  277.                 QApplication.restoreOverrideCursor()
  278.                 
  279.     
  280.     def onPrinterChange(self, printer_name):
  281.         if printer_name != self.cur_printer:
  282.             self.unlock()
  283.             self.lock(printer_name)
  284.             #utils.unlock(self.lock_file)
  285.             #ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % printer_name, True)
  286.             
  287.         ScrollView.onPrinterChange(self, printer_name)
  288.         
  289.     def unlock(self):
  290.         utils.unlock(self.lock_file)
  291.         
  292.     def lock(self, printer_name=None):
  293.         if printer_name is None:
  294.             printer_name = self.cur_printer
  295.             
  296.         ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % printer_name, True)
  297.         
  298.  
  299.  
  300.     # Event handler for adding files from a external print job (not during fax send thread)
  301.     def addFileFromJob(self, event, title, username, job_id, fax_file):
  302.         QApplication.setOverrideCursor(QApplication.waitCursor)
  303.         self.busy = True
  304.  
  305.         try:
  306.             f = file(fax_file, 'r')
  307.             header = f.read(fax.FILE_HEADER_SIZE)
  308.  
  309.             if len(header) != fax.FILE_HEADER_SIZE:
  310.                 log.error("Invalid fax file! (truncated header or no data)")
  311.                 sys.exit(1)
  312.                 
  313.             mg, version, total_pages, hort_dpi, vert_dpi, page_size, \
  314.                 resolution, encoding, reserved1, reserved2 = \
  315.                 struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
  316.  
  317.             log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  318.                       (mg, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  319.             
  320.             if total_pages > 0:
  321.                 mime_type = job_types.get(job_id, "application/hplip-fax")
  322.                 mime_type_desc = self.MIME_TYPES_DESC.get(mime_type, ('Unknown', 'n/a'))[0]
  323.                 log.debug("%s (%s)" % (mime_type, mime_type_desc))
  324.                 self.file_list.append((fax_file, mime_type, mime_type_desc, title, total_pages))
  325.                 self.prev_selected_file_path = fax_file
  326.             else:
  327.                 self.form.FailureUI("<b>Render Failure:</b><p>Rendered document contains no data.")
  328.  
  329.             self.updateFileList()
  330.  
  331.         finally:
  332.             self.busy = False
  333.             
  334.             if self.waitdlg is not None:
  335.                 self.waitdlg.hide()
  336.                 self.waitdlg.close()
  337.                 self.waitdlg = None            
  338.             
  339.             QApplication.restoreOverrideCursor()                
  340.  
  341.             
  342.     def add_fax_canceled(self):
  343.             pass
  344.  
  345.     #
  346.     # FILE LIST
  347.     #
  348.  
  349.     def addFileList(self):
  350.         widget = self.getWidget()
  351.  
  352.         layout37 = QGridLayout(widget,1,1,5,10,"layout37")
  353.  
  354.         self.addFilePushButton = PixmapLabelButton(widget, "list_add.png", 
  355.             "list_add-disabled.png", name='addFilePushButton')
  356.  
  357.         layout37.addWidget(self.addFilePushButton,2,0)
  358.  
  359.         self.removeFilePushButton = PixmapLabelButton(widget, 
  360.             "list_remove.png", "list_remove-disabled.png", name='removeFilePushButton')
  361.  
  362.         layout37.addWidget(self.removeFilePushButton,2,1)
  363.  
  364.         self.moveFileUpPushButton = PixmapLabelButton(widget, "up.png", 
  365.             "up-disabled.png", name='moveFileUpPushButton')
  366.  
  367.         layout37.addWidget(self.moveFileUpPushButton,2,2)
  368.  
  369.         self.moveFileDownPushButton = PixmapLabelButton(widget, "down.png", 
  370.             "down-disabled.png", name='moveFileDownPushButton')
  371.  
  372.         layout37.addWidget(self.moveFileDownPushButton,2,3)
  373.  
  374.         self.showTypesPushButton = PixmapLabelButton(widget, "mimetypes.png", 
  375.             None, name='showTypesPushButton')
  376.  
  377.         layout37.addWidget(self.showTypesPushButton,2,5)
  378.  
  379.  
  380.         self.fileListView = QListView(widget,"fileListView")
  381.         self.fileListView.addColumn(self.__tr("Order"))
  382.         self.fileListView.addColumn(self.__tr("Name"))
  383.         self.fileListView.addColumn(self.__tr("Type"))
  384.         self.fileListView.addColumn(self.__tr("Pages"))
  385.         self.fileListView.addColumn(self.__tr("Path"))
  386.         self.fileListView.setAllColumnsShowFocus(1)
  387.         self.fileListView.setShowSortIndicator(1)
  388.         self.fileListView.setColumnWidth(0, 100) # Order
  389.         self.fileListView.setColumnWidth(1, 150) # Name
  390.         self.fileListView.setColumnWidth(2, 100) # Type
  391.         self.fileListView.setColumnWidth(3, 100) # Pages
  392.         self.fileListView.setColumnWidth(4, 300) # Path
  393.         self.fileListView.setItemMargin(2)
  394.         self.fileListView.setSorting(-1)
  395.  
  396.         layout37.addMultiCellWidget(self.fileListView,1,1,0,5)
  397.  
  398.         spacer26 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
  399.         layout37.addItem(spacer26,2,4)
  400.  
  401.         self.addFilePushButton.setText(self.__tr("Add File..."))
  402.         self.showTypesPushButton.setText(self.__tr("Show Types..."))
  403.         self.removeFilePushButton.setText(self.__tr("Remove File"))
  404.         self.moveFileDownPushButton.setText(self.__tr("Move Down"))
  405.         self.moveFileUpPushButton.setText(self.__tr("Move Up"))
  406.  
  407.         self.removeFilePushButton.setEnabled(False)
  408.         self.moveFileDownPushButton.setEnabled(False)
  409.         self.moveFileUpPushButton.setEnabled(False)
  410.         self.connect(self.addFilePushButton, SIGNAL("clicked()"), self.addFile_clicked)
  411.         self.connect(self.removeFilePushButton, SIGNAL("clicked()"), self.removeFile_clicked)
  412.         self.connect(self.showTypesPushButton, SIGNAL("clicked()"), self.showFileTypes_clicked)
  413.  
  414.         self.connect(self.moveFileUpPushButton, SIGNAL("clicked()"), self.moveFileUp_clicked)
  415.         self.connect(self.moveFileDownPushButton, SIGNAL("clicked()"), self.moveFileDown_clicked)
  416.  
  417.         self.connect(self.fileListView,SIGNAL("rightButtonClicked(QListViewItem*,const QPoint&, int)"),self.fileListView_rightButtonClicked)
  418.  
  419.         self.connect(self.fileListView, SIGNAL("selectionChanged(QListViewItem*)"), self.fileListView_selectionChanged)
  420.  
  421.         self.addWidget(widget, "file_list", maximize=True)
  422.  
  423.     def fileListView_selectionChanged(self, i):
  424.         try:
  425.             self.prev_selected_file_path = i.path
  426.         except AttributeError:
  427.             pass
  428.         else:
  429.             flv = self.fileListView
  430.             selected_item = flv.selectedItem()
  431.             file_count = flv.childCount()
  432.             last_item = flv.firstChild()
  433.             while last_item.nextSibling():
  434.                 last_item = last_item.nextSibling()
  435.  
  436.             self.moveFileDownPushButton.setEnabled(file_count > 1 and selected_item is not last_item)
  437.             self.moveFileUpPushButton.setEnabled(file_count > 1 and selected_item is not flv.firstChild())
  438.  
  439.  
  440.     def moveFileUp_clicked(self):
  441.         try:
  442.             path = self.fileListView.selectedItem().path
  443.         except AttributeError:
  444.             return
  445.         else:
  446.             for i in range(1, len(self.file_list)):
  447.                 if self.file_list[i][0] == path:
  448.                     self.file_list[i-1],self.file_list[i] = self.file_list[i], self.file_list[i-1]
  449.  
  450.             self.updateFileList()
  451.  
  452.     def moveFileDown_clicked(self):
  453.         try:
  454.             path = self.fileListView.selectedItem().path
  455.         except AttributeError:
  456.             return
  457.         else:
  458.             for i in range(len(self.file_list) - 2, -1, -1):
  459.                 if self.file_list[i][0] == path:
  460.                     self.file_list[i], self.file_list[i+1] = self.file_list[i+1], self.file_list[i] 
  461.  
  462.             self.updateFileList()
  463.  
  464.  
  465.     def fileListView_rightButtonClicked(self, item, pos, col):
  466.         popup = QPopupMenu(self)
  467.  
  468.         popup.insertItem(QIconSet(load_pixmap('list_add', '16x16')),
  469.             self.__tr("Add File..."), self.addFile_clicked)
  470.  
  471.         if item is not None:
  472.             popup.insertItem(QIconSet(load_pixmap('list_remove', '16x16')), 
  473.                 self.__tr("Remove File"), self.removeFile_clicked)
  474.  
  475.             if self.fileListView.childCount() > 1:
  476.                 last_item = self.fileListView.firstChild()
  477.                 while last_item is not None and last_item.nextSibling():
  478.                     last_item = last_item.nextSibling()
  479.  
  480.                 if item is not self.fileListView.firstChild():
  481.                     popup.insertItem(QIconSet(load_pixmap('up', '16x16')), 
  482.                         self.__tr("Move Up"), self.moveFileUp_clicked)
  483.  
  484.                 if item is not last_item:
  485.                     popup.insertItem(QIconSet(load_pixmap('down', '16x16')),
  486.                         self.__tr("Move Down"), self.moveFileDown_clicked)
  487.  
  488.  
  489.         popup.insertSeparator(-1)
  490.         popup.insertItem(QIconSet(load_pixmap('mimetypes', '16x16')), 
  491.             self.__tr("Show File Types..."), self.showFileTypes_clicked)
  492.         
  493.         popup.popup(pos)
  494.  
  495.  
  496.     def addFile(self, path, title, mime_type, mime_type_desc, pages):
  497.         self.file_list.append((path, mime_type, mime_type_desc, title, pages))
  498.         self.prev_selected_file_path = path
  499.  
  500.         self.updateFileList()
  501.  
  502.     def processFile(self, path, title=''): # Process an arbitrary file ("Add file...")
  503.         path = os.path.realpath(path)
  504.         if not title:
  505.             title = os.path.basename(path)
  506.  
  507.         if os.path.exists(path) and os.access(path, os.R_OK):
  508.             mime_type = magic.mime_type(path)
  509.             mime_type_desc = mime_type
  510.  
  511.             if mime_type == 'application/hplip-fax':
  512.                 mime_type_desc = self.MIME_TYPES_DESC[mime_type][0]
  513.  
  514.                 fax_file_fd = file(path, 'r')
  515.                 header = fax_file_fd.read(fax.FILE_HEADER_SIZE)
  516.  
  517.                 mg, version, pages, hort_dpi, vert_dpi, page_size, \
  518.                     resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
  519.  
  520.                 if mg != 'hplip_g3':
  521.                     log.error("Invalid file header. Bad magic.")
  522.                     self.form.WarningUI(self.__tr("<b>Invalid HPLIP Fax file.</b><p>Bad magic!"))
  523.                     return
  524.  
  525.                 self.addFile(path, title, mime_type, mime_type_desc, pages)
  526.             
  527.             else:
  528.                 log.debug(repr(mime_type))
  529.                 try:
  530.                     mime_type_desc = self.MIME_TYPES_DESC[mime_type][0]
  531.                 except KeyError:
  532.                     self.form.WarningUI(self.__tr("<b>You are trying to add a file that cannot be directly faxed with this utility.</b><p>To print this file, use the print command in the application that created it."))
  533.                     return
  534.                 else:
  535.                     log.debug("Adding file: title='%s' file=%s mime_type=%s mime_desc=%s)" % (title, path, mime_type, mime_type_desc))
  536.  
  537.                     all_pages = True 
  538.                     page_range = ''
  539.                     page_set = 0
  540.                     #nup = 1
  541.  
  542.                     cups.resetOptions()
  543.  
  544.                     self.cups_printers = cups.getPrinters()
  545.  
  546.                     printer_state = cups.IPP_PRINTER_STATE_STOPPED
  547.                     for p in self.cups_printers:
  548.                         if p.name == self.cur_printer:
  549.                             printer_state = p.state
  550.  
  551.                     log.debug("Printer state = %d" % printer_state)
  552.  
  553.                     if printer_state == cups.IPP_PRINTER_STATE_IDLE:
  554.                         log.debug("Printing: %s on %s" % (path, self.cur_printer))
  555.                         sent_job_id = cups.printFile(self.cur_printer, path, os.path.basename(path))
  556.                         self.last_job_id = sent_job_id
  557.                         job_types[sent_job_id] = mime_type # save for later
  558.                         log.debug("Job ID=%d" % sent_job_id)  
  559.  
  560.                         QApplication.setOverrideCursor(QApplication.waitCursor)
  561.  
  562.                         self.waitdlg = WaitForm(0, self.__tr("Processing fax file..."), None, self, modal=1) # self.add_fax_canceled
  563.                         self.waitdlg.show()
  564.  
  565.                     else:
  566.                         self.form.FailureUI(self.__tr("<b>Printer '%1' is in a stopped or error state.</b><p>Check the printer queue in CUPS and try again.").arg(self.cur_printer))
  567.                         cups.resetOptions()
  568.                         return
  569.  
  570.                     cups.resetOptions()
  571.                     QApplication.restoreOverrideCursor()
  572.  
  573.         else:
  574.             self.form.FailureUI(self.__tr("<b>Unable to add file '%1' to file list (file not found or insufficient permissions).</b><p>Check the file name and try again.").arg(path))
  575.  
  576.  
  577.  
  578.     def updateFileList(self):
  579.         self.fileListView.clear()
  580.         temp = self.file_list[:]
  581.         temp.reverse()
  582.         order = len(temp)
  583.         last_item = None
  584.         selected_item = None
  585.  
  586.         for path, mime_type, mime_type_desc, title, pages in temp:
  587.             i = FileListViewItem(self.fileListView, str(order), title, mime_type_desc, str(pages), path)
  588.  
  589.             if not self.prev_selected_file_path or self.prev_selected_file_path == path:
  590.                 self.fileListView.setSelected(i, True)
  591.                 selected_item = i
  592.                 self.prev_selected_file_path = path
  593.  
  594.             order -= 1
  595.  
  596.         last_item = self.fileListView.firstChild()
  597.         while last_item is not None and last_item.nextSibling():
  598.             last_item = last_item.nextSibling()
  599.  
  600.         file_count = self.fileListView.childCount()
  601.         self.moveFileDownPushButton.setEnabled(file_count > 1 and selected_item is not last_item)
  602.         self.moveFileUpPushButton.setEnabled(file_count > 1 and selected_item is not self.fileListView.firstChild())
  603.         self.checkSendFaxButton()
  604.         self.removeFilePushButton.setEnabled(file_count > 0)
  605.  
  606.  
  607.  
  608.     def addFile_clicked(self):
  609.         workingDirectory = user_cfg.last_used.working_dir
  610.  
  611.         if not workingDirectory or not os.path.exists(workingDirectory):
  612.             workingDirectory = os.path.expanduser("~")
  613.  
  614.         log.debug("workingDirectory: %s" % workingDirectory)
  615.  
  616.         dlg = QFileDialog(workingDirectory, QString.null, None, None, True)
  617.  
  618.         dlg.setCaption("openfile")
  619.         dlg.setMode(QFileDialog.ExistingFile)
  620.         dlg.show()
  621.  
  622.         if dlg.exec_loop() == QDialog.Accepted:
  623.                 results = dlg.selectedFile()
  624.                 workingDirectory = unicode(dlg.dir().absPath())
  625.                 log.debug("results: %s" % results)
  626.                 log.debug("workingDirectory: %s" % workingDirectory)
  627.  
  628.                 user_cfg.last_used.working_dir = workingDirectory
  629.  
  630.                 if results:
  631.                     path = unicode(results)
  632.                     self.processFile(path)
  633.  
  634.  
  635.     def removeFile_clicked(self):
  636.         try:
  637.             path = self.fileListView.selectedItem().path
  638.         except AttributeError:
  639.             return
  640.         else:
  641.             temp = self.file_list[:]
  642.             index = 0
  643.             for p, t, d, x, g in temp:
  644.                 if p == path:
  645.                     del self.file_list[index]
  646.  
  647.                     if t == 'application/hplip-fax-coverpage':
  648.                         self.addCoverpagePushButton.setEnabled(coverpages_enabled)
  649.                         self.editCoverpagePushButton.setEnabled(False)
  650.  
  651.                     self.prev_selected_file_path = ''
  652.                     self.updateFileList()
  653.                     break
  654.  
  655.                 index += 1
  656.  
  657.  
  658.     def showFileTypes_clicked(self):
  659.         x = {}
  660.         for a in self.allowable_mime_types:
  661.             x[a] = self.MIME_TYPES_DESC.get(a, ('Unknown', 'n/a'))
  662.  
  663.         log.debug(x)
  664.         dlg = AllowableTypesDlg(x, self)
  665.         dlg.exec_loop()
  666.  
  667.  
  668.     #
  669.     # COVERPAGE
  670.     #
  671.  
  672.     def addCoverpage(self):
  673.         widget = self.getWidget()
  674.  
  675.         layout14 = QGridLayout(widget,1,1,5,10,"layout14")
  676.  
  677.         self.editCoverpagePushButton = PixmapLabelButton(widget, 
  678.             "edit.png", "edit-disabled.png", name='')
  679.  
  680.         layout14.addWidget(self.editCoverpagePushButton,0,1)
  681.  
  682.         self.addCoverpagePushButton = PixmapLabelButton(widget, 
  683.             "list_add.png", "list_add-disabled.png", name='')
  684.  
  685.         layout14.addWidget(self.addCoverpagePushButton,0,2)
  686.         spacer12_2 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
  687.         layout14.addItem(spacer12_2,0,0)
  688.  
  689.         self.editCoverpagePushButton.setText(self.__tr("Edit..."))
  690.         self.editCoverpagePushButton.setEnabled(False)
  691.  
  692.         self.addCoverpagePushButton.setText(self.__tr("Add..."))
  693.  
  694.         self.connect(self.editCoverpagePushButton,SIGNAL("clicked()"),self.editCoverpagePushButton_clicked)
  695.         self.connect(self.addCoverpagePushButton,SIGNAL("clicked()"),self.addCoverpagePushButton_clicked)
  696.  
  697.         self.addWidget(widget, "coverpage")
  698.  
  699.     def editCoverpagePushButton_clicked(self):
  700.         self.showCoverPageDlg()
  701.  
  702.     def addCoverpagePushButton_clicked(self):
  703.         if self.showCoverPageDlg():
  704.             self.file_list.insert(0, ('n/a', "application/hplip-fax-coverpage", 
  705.                 self.__tr("HP Fax Coverpage"), self.__tr("Cover Page"), 1))
  706.  
  707.             self.updateFileList()
  708.             self.addCoverpagePushButton.setEnabled(False)
  709.             self.editCoverpagePushButton.setEnabled(True)
  710.  
  711.     def showCoverPageDlg(self):
  712.         dlg = CoverpageForm(self.cover_page_name, self.preserve_formatting, parent=self)
  713.         dlg.messageTextEdit.setText(self.cover_page_message)
  714.         dlg.regardingTextEdit.setText(self.cover_page_re)
  715.  
  716.         if dlg.exec_loop() == QDialog.Accepted:
  717.  
  718.             self.cover_page_func, cover_page_png = dlg.data
  719.             self.cover_page_message = unicode(dlg.messageTextEdit.text())
  720.             self.cover_page_re = unicode(dlg.regardingTextEdit.text())
  721.             self.cover_page_name = dlg.coverpage_name
  722.             self.preserve_formatting = dlg.preserve_formatting
  723.             return True
  724.  
  725.         return False
  726.  
  727.  
  728.  
  729.     #
  730.     # RECIPIENT LIST
  731.     #
  732.  
  733.     def addRecipientList(self):
  734.         widget = self.getWidget()
  735.  
  736.         layout9 = QGridLayout(widget,1,1,5,10,"layout9")
  737.  
  738.         self.moveDownPushButton = PixmapLabelButton(widget, 
  739.             "down_user.png", "down_user-disabled.png", name='')
  740.  
  741.         layout9.addWidget(self.moveDownPushButton,1,2)
  742.  
  743.         self.recipientListView = QListView(widget,"recipientListView")
  744.         self.recipientListView.addColumn(self.__tr("Order"))
  745.         self.recipientListView.addColumn(self.__tr("Name"))
  746.         self.recipientListView.addColumn(self.__tr("Fax Number"))
  747.         self.recipientListView.addColumn(self.__tr("Notes"))
  748.  
  749.         self.recipientListView.setAllColumnsShowFocus(1)
  750.         self.recipientListView.setShowSortIndicator(1)
  751.         self.recipientListView.setColumnWidth(0, 100) # Order
  752.         self.recipientListView.setColumnWidth(1, 150) # Name
  753.         self.recipientListView.setColumnWidth(2, 200) # Fax Number
  754.         self.recipientListView.setColumnWidth(3, 250) # Notes
  755.         self.recipientListView.setItemMargin(2)
  756.         self.recipientListView.setSorting(-1)
  757.  
  758.         widget.setMaximumHeight(250)
  759.  
  760.         layout9.addMultiCellWidget(self.recipientListView,0,0,0,4)
  761.  
  762.         self.fabPushButton = PixmapLabelButton(widget, 
  763.                     "fab", None, name='') 
  764.  
  765.         layout9.addWidget(self.fabPushButton,1,4)
  766.  
  767.         self.removeRecipientPushButton = PixmapLabelButton(widget, 
  768.             "remove_user.png", "remove_user-disabled.png", name='')
  769.  
  770.         self.removeRecipientPushButton.setEnabled(1)
  771.  
  772.         layout9.addWidget(self.removeRecipientPushButton,1,0)
  773.         spacer10 = QSpacerItem(20,20,QSizePolicy.MinimumExpanding,QSizePolicy.Minimum)
  774.         layout9.addItem(spacer10,1,3)
  775.  
  776.         self.moveUpPushButton = PixmapLabelButton(widget, 
  777.             "up_user.png", "up_user-disabled.png", name='')
  778.  
  779.         layout9.addWidget(self.moveUpPushButton,1,1)
  780.  
  781.         self.moveDownPushButton.setEnabled(False)
  782.         self.moveUpPushButton.setEnabled(False)
  783.         self.removeRecipientPushButton.setEnabled(False)
  784.  
  785.         self.moveDownPushButton.setText(self.__tr("Move Down"))
  786.         self.fabPushButton.setText(self.__tr("Fax Address Book..."))
  787.         self.removeRecipientPushButton.setText(self.__tr("Remove"))
  788.         self.moveUpPushButton.setText(self.__tr("Move Up"))
  789.  
  790.  
  791.         self.connect(self.recipientListView,SIGNAL("rightButtonClicked(QListViewItem*,const QPoint&,int)"),self.recipientListView_rightButtonClicked)
  792.  
  793.         self.connect(self.removeRecipientPushButton,SIGNAL("clicked()"),self.removeRecipientPushButton_clicked)
  794.         self.connect(self.moveUpPushButton,SIGNAL("clicked()"),self.moveUpPushButton_clicked)
  795.         self.connect(self.moveDownPushButton,SIGNAL("clicked()"),self.moveDownPushButton_clicked)
  796.         self.connect(self.fabPushButton,SIGNAL("clicked()"),self.fabPushButton_clicked)
  797.  
  798.         self.connect(self.recipientListView, SIGNAL("selectionChanged(QListViewItem*)"), self.recipientListView_selectionChanged)
  799.  
  800.         self.addWidget(widget, "recipient_list", maximize=False)
  801.  
  802.  
  803.     def recipientListView_selectionChanged(self, i):
  804.         try:
  805.             self.prev_selected_recipient = i.name
  806.         except AttributeError:
  807.             pass
  808.         else:
  809.             rlv = self.recipientListView
  810.             selected_item = rlv.selectedItem()
  811.             recipient_count = rlv.childCount()
  812.             last_item = rlv.firstChild()
  813.             while last_item.nextSibling():
  814.                 last_item = last_item.nextSibling()
  815.  
  816.             self.moveDownPushButton.setEnabled(recipient_count > 1 and selected_item is not last_item)
  817.             self.moveUpPushButton.setEnabled(recipient_count > 1 and selected_item is not rlv.firstChild())
  818.  
  819.  
  820.  
  821.     def updateRecipientList(self):
  822.         self.recipientListView.clear()
  823.         temp = self.recipient_list[:]
  824.         temp.reverse()
  825.         last_item = None
  826.         selected_item = None
  827.         order = len(temp)
  828.  
  829.         for name in temp:
  830.             entry = self.db.get(name)
  831.             # TODO: If entry was in list prior to name change in hp-fab, 
  832.             # this code will remove it instead of following the name change
  833.             # Ref: CDP-1675
  834.             if entry is not None:
  835.                 i = RecipientListViewItem(self.recipientListView, str(order), name, entry['fax'], entry['notes'])
  836.     
  837.                 if not self.prev_selected_recipient or self.prev_selected_recipient == name:
  838.                     self.recipientListView.setSelected(i, True)
  839.                     selected_item = i
  840.                     self.prev_selected_recipient = name
  841.     
  842.                 order -= 1
  843.  
  844.         last_item = self.recipientListView.firstChild()
  845.         while last_item is not None and last_item.nextSibling():
  846.             last_item = last_item.nextSibling()
  847.  
  848.         child_count = self.recipientListView.childCount()
  849.         self.removeRecipientPushButton.setEnabled(child_count > 0)
  850.         self.moveDownPushButton.setEnabled(child_count > 1 and selected_item is not last_item)
  851.         self.moveUpPushButton.setEnabled(child_count > 1 and selected_item is not self.recipientListView.firstChild())
  852.  
  853.         self.checkSendFaxButton()
  854.  
  855.  
  856.  
  857.     def recipientListView_rightButtonClicked(self, item, pos, col):
  858.         self.ind_map = {}
  859.         self.grp_map = {}
  860.         popup = QPopupMenu(self)
  861.         ind = QPopupMenu(popup)
  862.         grp = QPopupMenu(popup)
  863.  
  864.         all_entries = self.db.get_all_records()
  865.         if all_entries:
  866.             popup.insertItem(QIconSet(load_pixmap('add_user', '16x16')),
  867.                 self.__tr("Add Individual"), ind)
  868.  
  869.             for e, v in all_entries.items():
  870.                 self.ind_map[ind.insertItem(QIconSet(load_pixmap('add_user', '16x16')), e, None)] = e
  871.  
  872.         all_groups = self.db.get_all_groups()
  873.         if all_groups:
  874.             popup.insertItem(QIconSet(load_pixmap('add_users', '16x16')),
  875.                 self.__tr("Add Group"), grp)
  876.  
  877.             for g in all_groups:
  878.                 self.grp_map[grp.insertItem(QIconSet(load_pixmap('add_users', '16x16')), 
  879.                     g, None)] = g
  880.  
  881.         if item is not None:
  882.             popup.insertSeparator(-1)
  883.  
  884.             popup.insertItem(QIconSet(load_pixmap('remove_user', '16x16')), 
  885.                 self.__tr("Remove"), self.removeRecipientPushButton_clicked)
  886.  
  887.             if self.recipientListView.childCount() > 1:
  888.                 last_item = self.recipientListView.firstChild()
  889.                 while last_item is not None and last_item.nextSibling():
  890.                     last_item = last_item.nextSibling()
  891.  
  892.                 if item is not self.recipientListView.firstChild():
  893.                     popup.insertItem(QIconSet(load_pixmap('up_user', '16x16')), 
  894.                         self.__tr("Move Up"), self.moveUpPushButton_clicked)
  895.  
  896.                 if item is not last_item:
  897.                     popup.insertItem(QIconSet(load_pixmap('down_user', '16x16')), 
  898.                         self.__tr("Move Down"), self.moveDownPushButton_clicked)
  899.  
  900.         popup.insertSeparator(-1)
  901.         popup.insertItem(QIconSet(load_pixmap('fab', '16x16')), 
  902.             self.__tr("Fax Address Book..."), self.fabPushButton_clicked)
  903.  
  904.         self.connect(ind, SIGNAL("activated(int)"), self.ind_popup_activated)
  905.         self.connect(grp, SIGNAL("activated(int)"), self.grp_popup_activated)
  906.  
  907.         popup.popup(pos)
  908.  
  909.  
  910.     def ind_popup_activated(self, i):
  911.         self.addRecipient(self.ind_map[i])
  912.  
  913.     def grp_popup_activated(self, i):
  914.         self.addRecipient(self.grp_map[i], True)
  915.  
  916.     def moveUpPushButton_clicked(self):
  917.         try:
  918.             name = self.recipientListView.selectedItem().name
  919.         except AttributeError:
  920.             return
  921.         else:
  922.             utils.list_move_up(self.recipient_list, name)
  923.             self.updateRecipientList()
  924.  
  925.     def moveDownPushButton_clicked(self):
  926.         try:
  927.             name = self.recipientListView.selectedItem().name
  928.         except AttributeError:
  929.             return
  930.         else:
  931.             utils.list_move_down(self.recipient_list, name)
  932.             self.updateRecipientList()
  933.  
  934.  
  935.     def fabPushButton_clicked(self):
  936.         log.debug(self.cmd_fab)
  937.         #print self.cmd_fab
  938.         cmd = ''.join([self.cur_device.device_vars.get(x, x) \
  939.                          for x in self.cmd_fab.split('%')])
  940.         log.debug(cmd)
  941.  
  942.         path = cmd.split()[0]
  943.         args = cmd.split()
  944.  
  945.         self.CleanupChildren()
  946.         #os.spawnvp(os.P_NOWAIT, path, args) 
  947.         os.system(cmd)
  948.  
  949.         self.db.load()
  950.         self.updateRecipientList()
  951.         self.updateRecipientCombos()
  952.  
  953.  
  954.     def CleanupChildren(self):
  955.         log.debug("Cleaning up child processes.")
  956.         try:
  957.             os.waitpid(-1, os.WNOHANG)
  958.         except OSError:
  959.             pass
  960.  
  961.  
  962.     def removeRecipientPushButton_clicked(self):
  963.         try:
  964.             name = self.recipientListView.selectedItem().name
  965.         except AttributeError:
  966.             return
  967.         else:
  968.             temp = self.recipient_list[:]
  969.             index = 0
  970.             for n in temp:
  971.                 if name == n:
  972.                     del self.recipient_list[index]
  973.  
  974.                     self.prev_selected_recipient = ''
  975.                     self.updateRecipientList()
  976.                     break
  977.  
  978.                 index += 1
  979.  
  980.  
  981.  
  982.     #
  983.     # ADD FROM ADDRESS BOOK
  984.     #
  985.  
  986.     def addRecipientAddFromFAB(self):
  987.         widget = self.getWidget()
  988.  
  989.         layout13 = QGridLayout(widget,1,1,5,10,"layout13")
  990.  
  991.         self.groupComboBox = QComboBox(0,widget,"groupComboBox")
  992.         self.groupComboBox.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Fixed,0,0,self.groupComboBox.sizePolicy().hasHeightForWidth()))
  993.  
  994.         layout13.addWidget(self.groupComboBox,1,2)
  995.         spacer12 = QSpacerItem(20,20,QSizePolicy.Preferred,QSizePolicy.Minimum)
  996.         layout13.addItem(spacer12,1,1)
  997.  
  998.         self.textLabel1 = QLabel(widget,"textLabel1")
  999.         self.textLabel1.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Preferred,0,0,self.textLabel1.sizePolicy().hasHeightForWidth()))
  1000.  
  1001.         layout13.addWidget(self.textLabel1,0,0)
  1002.  
  1003.         self.individualComboBox = QComboBox(0,widget,"individualComboBox")
  1004.         self.individualComboBox.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Fixed,0,0,self.individualComboBox.sizePolicy().hasHeightForWidth()))
  1005.  
  1006.         layout13.addWidget(self.individualComboBox,0,2)
  1007.  
  1008.         self.textLabel2 = QLabel(widget,"textLabel2")
  1009.         self.textLabel2.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Preferred,0,0,self.textLabel2.sizePolicy().hasHeightForWidth()))
  1010.  
  1011.         layout13.addWidget(self.textLabel2,1,0)
  1012.         spacer11 = QSpacerItem(30,20,QSizePolicy.Preferred,QSizePolicy.Minimum)
  1013.         layout13.addItem(spacer11,0,1)
  1014.  
  1015.         self.addGroupPushButton = PixmapLabelButton(widget, 
  1016.                     "add_users.png", "add_users-disabled.png", name='addGroupPushButton')
  1017.  
  1018.         layout13.addWidget(self.addGroupPushButton,1,3)
  1019.  
  1020.         self.addIndividualPushButton = PixmapLabelButton(widget, 
  1021.                     "add_user.png", "add_user-disabled.png", name='addIndividualPushButton')
  1022.  
  1023.  
  1024.         layout13.addWidget(self.addIndividualPushButton,0,3)
  1025.  
  1026.         self.textLabel1.setText(self.__tr("Add an <b>individual </b>from the fax address book:"))
  1027.         self.textLabel2.setText(self.__tr("Add a <b>group</b> from the fax address book:"))
  1028.         self.addGroupPushButton.setText(self.__tr("Add"))
  1029.         self.addIndividualPushButton.setText(self.__tr("Add"))
  1030.  
  1031.         self.connect(self.addIndividualPushButton,SIGNAL("clicked()"),self.addIndividualPushButton_clicked)
  1032.         self.connect(self.addGroupPushButton,SIGNAL("clicked()"),self.addGroupPushButton_clicked)
  1033.  
  1034.  
  1035.         self.addWidget(widget, "recipient_add_from_fab")
  1036.  
  1037.     def addIndividualPushButton_clicked(self):
  1038.         self.addRecipient(unicode(self.individualComboBox.currentText()))
  1039.  
  1040.     def addGroupPushButton_clicked(self):
  1041.         self.addRecipient(unicode(self.groupComboBox.currentText()), True)
  1042.  
  1043.     def addRecipient(self, name, is_group=False):
  1044.         if is_group:
  1045.             for i in self.db.group_members(name):
  1046.             #for i in self.db.GroupEntries(name):
  1047.                 self.recipient_list.append(i)
  1048.                 self.prev_selected_recipient = self.recipient_list[-1]
  1049.         else:
  1050.             self.recipient_list.append(name)
  1051.             self.prev_selected_recipient = name
  1052.  
  1053.         self.updateRecipientList()
  1054.  
  1055.     def updateRecipientCombos(self):
  1056.         # Individuals
  1057.         self.individualComboBox.clear()
  1058.         all_entries = self.db.get_all_records()
  1059.         self.addIndividualPushButton.setEnabled(len(all_entries))
  1060.  
  1061.         for e, v in all_entries.items():
  1062.             self.individualComboBox.insertItem(e)
  1063.  
  1064.         # Groups
  1065.         self.groupComboBox.clear()
  1066.         all_groups = self.db.get_all_groups()
  1067.         self.addGroupPushButton.setEnabled(len(all_groups))
  1068.  
  1069.         for g in all_groups:
  1070.             self.groupComboBox.insertItem(g)
  1071.  
  1072.  
  1073.     #
  1074.     # QUICK ADD
  1075.     #
  1076.  
  1077.     def addRecipientQuickAdd(self):
  1078.         widget = self.getWidget()
  1079.  
  1080.         layout12 = QGridLayout(widget,1,1,5,10,"layout12")
  1081.         self.quickAddFaxLineEdit = QLineEdit(widget,"quickAddFaxLineEdit")
  1082.  
  1083.         self.quickAddFaxLineEdit.setValidator(PhoneNumValidator(self.quickAddFaxLineEdit))
  1084.         layout12.addWidget(self.quickAddFaxLineEdit,0,3)
  1085.  
  1086.         self.quickAddNameLineEdit = QLineEdit(widget,"quickAddNameLineEdit")
  1087.         layout12.addWidget(self.quickAddNameLineEdit,0,1)
  1088.  
  1089.         self.textLabel4 = QLabel(widget,"textLabel4")
  1090.         layout12.addWidget(self.textLabel4,0,0)
  1091.  
  1092.         self.quickAddPushButton = PixmapLabelButton(widget, 
  1093.                     "add_user_quick.png", "add_user_quick-disabled.png", name='quickAddPushButton')
  1094.  
  1095.         layout12.addWidget(self.quickAddPushButton,0,4)
  1096.  
  1097.         self.textLabel5 = QLabel(widget,"textLabel5")
  1098.         layout12.addWidget(self.textLabel5,0,2)
  1099.  
  1100.         self.textLabel4.setText(self.__tr("Name:"))
  1101.         self.quickAddPushButton.setText(self.__tr("Add"))
  1102.         self.textLabel5.setText(self.__tr("Fax Number:"))
  1103.  
  1104.         self.quickAddPushButton.setEnabled(False)
  1105.  
  1106.         self.connect(self.quickAddPushButton,SIGNAL("clicked()"),self.quickAddPushButton_clicked)
  1107.         self.connect(self.quickAddNameLineEdit,SIGNAL("textChanged(const QString&)"),self.quickAddNameLineEdit_textChanged)
  1108.         self.connect(self.quickAddFaxLineEdit,SIGNAL("textChanged(const QString&)"),self.quickAddFaxLineEdit_textChanged)
  1109.  
  1110.         self.addWidget(widget, "recipient_quick_add")
  1111.  
  1112.  
  1113.     def quickAddPushButton_clicked(self):
  1114.  
  1115.         # TODO: Check for duplicate already in FAB
  1116.  
  1117.         name =  unicode(self.quickAddNameLineEdit.text())
  1118.         self.db.set(name, u'', u'', u'', unicode(self.quickAddFaxLineEdit.text()), [], self.__tr('Added with Quick Add'))
  1119.         self.db.save()
  1120.         self.addRecipient(name)
  1121.  
  1122.         self.quickAddNameLineEdit.setText("")
  1123.         self.quickAddFaxLineEdit.setText("")
  1124.  
  1125.     def quickAddNameLineEdit_textChanged(self, s):
  1126.         self.quickAddPushButton.setEnabled(len(s) and len(self.quickAddFaxLineEdit.text()))
  1127.  
  1128.     def quickAddFaxLineEdit_textChanged(self, s):
  1129.         self.quickAddPushButton.setEnabled(len(self.quickAddNameLineEdit.text()) and len(s))
  1130.  
  1131.     def checkSendFaxButton(self):
  1132.         self.faxButton.setEnabled(len(self.file_list) and len(self.recipient_list))
  1133.  
  1134.     def faxButton_clicked(self):
  1135.         self.check_timer.stop()
  1136.         phone_num_list = []
  1137.  
  1138.         log.debug("Current printer=%s" % self.cur_printer)
  1139.         ppd_file = cups.getPPD(self.cur_printer)
  1140.  
  1141.         if ppd_file is not None and os.path.exists(ppd_file):
  1142.             if file(ppd_file, 'r').read().find('HP Fax') == -1:
  1143.                 self.form.FailureUI(self.__tr("<b>Fax configuration error.</b><p>The CUPS fax queue for '%1' is incorrectly configured.<p>Please make sure that the CUPS fax queue is configured with the 'HPLIP Fax' Model/Driver.").arg(self.cur_printer))
  1144.                 return
  1145.  
  1146.         QApplication.setOverrideCursor(QApplication.waitCursor)
  1147.  
  1148.         self.dev = fax.getFaxDevice(self.cur_device.device_uri, 
  1149.                                    self.cur_printer, None,
  1150.                                    self.cur_device.mq['fax-type'])
  1151.  
  1152.         try:
  1153.             try:
  1154.                 self.dev.open()
  1155.             except Error, e:
  1156.                 log.warn(e.msg)
  1157.  
  1158.             try:
  1159.                 self.dev.queryDevice(quick=True)
  1160.             except Error, e:
  1161.                 log.error("Query device error (%s)." % e.msg)
  1162.                 self.dev.error_state = ERROR_STATE_ERROR
  1163.  
  1164.         finally:
  1165.             self.dev.close()
  1166.             QApplication.restoreOverrideCursor()
  1167.  
  1168.         if self.dev.error_state > ERROR_STATE_MAX_OK and \
  1169.             self.dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
  1170.             
  1171.             self.form.FailureUI(self.__tr("<b>Device is busy or in an error state (code=%1)</b><p>Please wait for the device to become idle or clear the error and try again.").arg(self.cur_device.status_code))
  1172.             return
  1173.  
  1174.         # Check to make sure queue in CUPS is idle
  1175.         self.cups_printers = cups.getPrinters()
  1176.         for p in self.cups_printers:
  1177.             if p.name == self.cur_printer:
  1178.                 if p.state == cups.IPP_PRINTER_STATE_STOPPED:
  1179.                     self.form.FailureUI(self.__tr("<b>The CUPS queue for '%1' is in a stopped or busy state.</b><p>Please check the queue and try again.").arg(self.cur_printer))
  1180.                     return
  1181.                 break
  1182.  
  1183.         log.debug("Recipient list:")
  1184.  
  1185.         for p in self.recipient_list:
  1186.             entry = self.db.get(p)
  1187.             phone_num_list.append(entry)
  1188.             log.debug("Name=%s Number=%s" % (entry["name"], entry["fax"]))
  1189.  
  1190.         log.debug("File list:")
  1191.  
  1192.  
  1193.         for f in self.file_list:
  1194.             log.debug(unicode(f))
  1195.  
  1196.         self.busy = True
  1197.  
  1198.         self.dev.sendEvent(EVENT_START_FAX_JOB, self.cur_printer, 0, '') 
  1199.     
  1200.         if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message, 
  1201.                                   self.cover_page_re, self.cover_page_func, self.preserve_formatting,
  1202.                                   self.cur_printer, self.update_queue, self.event_queue):
  1203.  
  1204.             self.form.FailureUI(self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
  1205.             self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.cur_printer, 0, '')
  1206.             self.busy = False
  1207.             return
  1208.  
  1209.  
  1210.         self.waitdlg = WaitForm(0, self.__tr("Initializing..."), self.send_fax_canceled, self, modal=1)
  1211.         self.waitdlg.show()
  1212.  
  1213.         self.send_fax_timer = QTimer(self, "SendFaxTimer")
  1214.         self.connect(self.send_fax_timer, SIGNAL('timeout()'), self.send_fax_timer_timeout)
  1215.         self.send_fax_timer.start(1000) # 1 sec UI updates
  1216.  
  1217.     def send_fax_canceled(self):
  1218.         self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
  1219.         self.dev.sendEvent(EVENT_FAX_JOB_CANCELED, self.cur_printer, 0, '')
  1220.  
  1221.     def send_fax_timer_timeout(self):
  1222.         while self.update_queue.qsize():
  1223.             try:
  1224.                 status, page_num, phone_num = self.update_queue.get(0)
  1225.             except Queue.Empty:
  1226.                 break
  1227.  
  1228.             if status == fax.STATUS_IDLE:
  1229.                 self.busy = False
  1230.                 self.send_fax_timer.stop()
  1231.  
  1232.                 if self.waitdlg is not None:
  1233.                     self.waitdlg.hide()
  1234.                     self.waitdlg.close()
  1235.                     self.waitdlg = None
  1236.  
  1237.             elif status == fax.STATUS_PROCESSING_FILES:
  1238.                 self.waitdlg.setMessage(self.__tr("Processing page %1...").arg(page_num))
  1239.  
  1240.             elif status == fax.STATUS_DIALING:
  1241.                 self.waitdlg.setMessage(self.__tr("Dialing %1...").arg(phone_num))
  1242.  
  1243.             elif status == fax.STATUS_CONNECTING:
  1244.                 self.waitdlg.setMessage(self.__tr("Connecting to %1...").arg(phone_num))
  1245.  
  1246.             elif status == fax.STATUS_SENDING:
  1247.                 self.waitdlg.setMessage(self.__tr("Sending page %1 to %2...").arg(page_num).arg(phone_num))
  1248.  
  1249.             elif status == fax.STATUS_CLEANUP:
  1250.                 self.waitdlg.setMessage(self.__tr("Cleaning up..."))
  1251.  
  1252.             elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
  1253.                 self.busy = False
  1254.                 self.send_fax_timer.stop()
  1255.  
  1256.                 if self.waitdlg is not None:
  1257.                     self.waitdlg.hide()
  1258.                     self.waitdlg.close()
  1259.                     self.waitdlg = None
  1260.  
  1261.                 if status  == fax.STATUS_ERROR:
  1262.                     self.form.FailureUI(self.__tr("<b>Fax send error.</b><p>"))
  1263.                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.cur_printer, 0, '')
  1264.  
  1265.                 elif status == fax.STATUS_BUSY:
  1266.                     self.form.FailureUI(self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
  1267.                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.cur_printer, 0, '')
  1268.  
  1269.                 elif status == fax.STATUS_COMPLETED:
  1270.                     self.dev.sendEvent(EVENT_END_FAX_JOB, self.cur_printer, 0, '')
  1271.  
  1272.                     self.funcButton_clicked()
  1273.  
  1274.  
  1275.     def cleanup(self):
  1276.         self.unlock()
  1277.         
  1278.         if fax_enabled:
  1279.             self.check_timer.stop()
  1280.  
  1281.     def funcButton_clicked(self):
  1282.         self.cleanup()
  1283.         self.form.close()
  1284.  
  1285.     def __tr(self,s,c = None):
  1286.         return qApp.translate("ScrollFaxView",s,c)
  1287.